
#region Imports

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;

using Justa.Data;
using Justa.Reflection;
using Justa.Data.Entity;

using PersistenceContext = System.Collections.Generic.KeyValuePair<string, Justa.Data.ColumnType>;

#endregion

namespace Justa.Data.Reflection
{
    /// <summary>
    /// 
    /// </summary>
    /// 
    internal class EntityReflector
    {
        /// <summary>
        /// 
        /// </summary>
        /// 
        public EntityReflector()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="entity"></param>
        /// 
        /// <returns></returns>
        /// 
        public static Object GetValueOfJoinColumn(Object entity)
        {
            EntityDefinition definition = GetEntityDefinition(entity.GetType());

            foreach (PersistenceContext columnType in definition.ColumnTypes)
            {
                if (columnType.Value.ColumnTypeEnum.Equals(ColumnTypeEnum.JOIN))
                {
                    Object association = definition.EntityType.GetField(columnType.Value.Property).GetValue(entity);
                    EntityDefinition associationDefinition = EntityReflector.GetEntityDefinition(association.GetType());

                    foreach (PersistenceContext joinColumnType in associationDefinition.ColumnTypes)
                    {
                        if (joinColumnType.Value.ColumnTypeEnum.Equals(ColumnTypeEnum.ID))
                        {
                            return association.GetType().GetField(joinColumnType.Key).GetValue(association);
                        }
                    }
                    continue;
                }
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="type"></param>
        /// 
        /// <returns></returns>
        /// 
        public static IDictionary<string, ColumnType> Translater(Type type)
        {
            if (type == null)
            {
                return new Dictionary<string, ColumnType>();
            }

            if (AttributeReflector.GetSingleAttribute<EntityAttribute>(type) == null)
            {
                // logger.Warn("specified type is not persistent (missing Entity attribute).");
                return new Dictionary<string, ColumnType>();
            }

            IDictionary<string, ColumnType> results = new Dictionary<string, ColumnType>();

            foreach (FieldInfo fieldInfo in type.GetFields())
            {
                // process pk
                object[] attributes = fieldInfo.GetCustomAttributes(typeof(IdAttribute), false);
                if (attributes.Length != 0)
                {
                    results.Add(fieldInfo.Name, new ColumnType(fieldInfo.Name, ((IdAttribute)attributes[0]).Name, ColumnTypeEnum.ID));
                    continue;
                }

                // process column
                object[] attributes1 = fieldInfo.GetCustomAttributes(typeof(ColumnAttribute), false);
                if (attributes1.Length != 0)
                {
                    results.Add(fieldInfo.Name, new ColumnType(fieldInfo.Name, ((ColumnAttribute)attributes1[0]).Name, ColumnTypeEnum.COLUMN));
                    continue;
                }

                // process join column
                object[] attributes2 = fieldInfo.GetCustomAttributes(typeof(JoinColumnAttribute), false);
                if (attributes2.Length != 0)
                {
                    results.Add(fieldInfo.Name, new ColumnType(fieldInfo.Name, ((JoinColumnAttribute)attributes2[0]).Name, ColumnTypeEnum.JOIN));
                }
            }

            return results;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="reader"></param>
        /// <param name="type"></param>
        /// 

        public static object Transalate(IDataReader reader, Type type)
        {
            object entity = Activator.CreateInstance(type);
            IDictionary<string, ColumnType> columns = Translater(type);

            while (reader.Read())
            {
                foreach (PersistenceContext context in columns)
                {
                    Console.WriteLine(string.Format("Entity: {0}, mapping field: {1} to column: {2}...", type, context.Key, context.Value.Column));

                    if (reader[context.Value.Column] != null)
                    {
                        Console.WriteLine("Found tpe: " + context.Value.ColumnTypeEnum);
                        if (context.Value.ColumnTypeEnum.Equals(ColumnTypeEnum.JOIN))
                        {
                            Console.WriteLine("Found join column will now move on...");
                            continue;
                        }
                        else
                        {
                            entity.GetType().GetField(context.Key).SetValue(entity, reader[context.Value.Column].ToString());
                        }
                    }

                }
            }

            return entity;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="entityType"></param>
        /// 
        /// <returns></returns>
        /// 
        public static EntityDefinition GetEntityDefinition(Type entity)
        {
            EntityDefinition defintion = new EntityDefinition();

            defintion.EntityAttribute = AttributeReflector.GetSingleAttribute<EntityAttribute>(entity);
            defintion.IdAttribute = AttributeReflector.GetSingleFieldAttribute<IdAttribute>(entity);
            defintion.JoinColumnAttribute = AttributeReflector.GetSingleFieldAttribute<JoinColumnAttribute>(entity);
            defintion.BeforeSaveAttribute = AttributeReflector.GetSingleMethodAttribute<BeforeSaveAttribute>(entity);
            defintion.BeforeUpdateAttribute = AttributeReflector.GetSingleMethodAttribute<BeforeUpdateAttribute>(entity);
            defintion.BeforeDeleteAttribute = AttributeReflector.GetSingleMethodAttribute<BeforeDeleteAttribute>(entity);
            defintion.AfterSaveAttribute = AttributeReflector.GetSingleMethodAttribute<AfterSaveAttribute>(entity);
            defintion.AfterUpdateAttribute = AttributeReflector.GetSingleMethodAttribute<AfterUpdateAttribute>(entity);
            defintion.AfterDeleteAttribute = AttributeReflector.GetSingleMethodAttribute<AfterDeleteAttribute>(entity);
            defintion.ColumnTypes = Translater(entity);
            defintion.EntityType = entity;

            return defintion;
        }
    }
}
